from datetime import datetime

from django.db import models

from textile import textile

import kangapy4.blog.util as util

#
# Utility classes
#

class K:
    """ From http://code.djangoproject.com/wiki/CookBookChoicesContantsClass 
    """
    def __init__(self, label=None, **kwargs):
        assert(len(kwargs) == 1)
        for k, v in kwargs.items():
            self.id = k
            self.v = v
        self.label = label or self.id

class Constants:
    """ From http://code.djangoproject.com/wiki/CookBookChoicesContantsClass 
    """
    def __init__(self, *args):
        self.klist = args
        for k in self.klist:
            setattr(self, k.id, k.v)

    def choices(self, with_blank=None):
        result = [(k.v, k.label) for k in self.klist]
        if with_blank is not None:
            result = [('', with_blank)] + result
        return result

    def is_valid_value(self, val):
        return bool(filter(lambda k: val == k.v, self.klist))

def as_html(text, transform):
    """ The text of this entry, transformed into HTML.
    """
    if transform == 'C':
        return util.cook(text)
    if transform == 'C2':
        return util.cook2(text)
    elif transform =='T':
        return textile(text.encode('utf8'))
    else:
        return text


#
# Constant definitions
#

ALLOW_COMMENTS = Constants(
        K('No', no='N'),
        K('Open', open='O'),
        K('Closed', closed='C')
    )


PUBLISH_STATES = Constants(
        K('Draft', draft='D'),
        K('Published', published='P'),
        K('Deleted', deleted='X')
    )

TRANSFORM_NAMES = Constants(
        K('Textile', textile='T'),
        K('Raw', raw='R'),
        K('Cooked', cooked='C'),
        K('Cooked2', cooked2='C2')
    )

# How many days before a post is 'older'
RECENT_DAYS = 14


#
# Model classes
# 

class Category(models.Model):
    """ Tags for BlogEntries
    """
    name = models.CharField(maxlength=200, unique=True)
    description = models.TextField(maxlength=2000, blank=True)
    super_category = models.ForeignKey('Category', null=True, blank=True)

    def __str__(self):
        """ Human readable representation. """
        return self.name

    class Admin:
        list_display = ('name', 'description')

    class Meta:
        ordering = ['name']
        verbose_name_plural= "categories"


class BlogEntryManager(models.Manager):
    """ The manager for blog entries.
    """

    def published(self):
        return self.all().filter(state = PUBLISH_STATES.published)
    

class BlogEntry(models.Model):
    """ A single blog entry.
    """
    objects = BlogEntryManager()

    created = models.DateTimeField(blank=True, null=True)
    modified = models.DateTimeField(blank=True, null=True)

    # XXX Slug must be unique on a given created date
    # Don't think this can be expressed in Django when we only have a datetime
    slug = models.SlugField(prepopulate_from=('title',))

    state = models.CharField(maxlength=1, blank=True, 
            choices=PUBLISH_STATES.choices(), default=PUBLISH_STATES.draft)
    title = models.CharField(maxlength=200)
    text = models.TextField(maxlength=50000)
    transform = models.CharField(maxlength=2, 
            choices=TRANSFORM_NAMES.choices(), default=TRANSFORM_NAMES.textile)
    excerpt = models.CharField(maxlength=1000)
    author = models.CharField(maxlength=200)
    mtId = models.IntegerField(blank=True, null=True)
    allow_comments = models.CharField(maxlength=1, 
            choices=ALLOW_COMMENTS.choices(), default=ALLOW_COMMENTS.open)
    categories = models.ManyToManyField(Category, blank=True)

    def calc_new_comment_state(self):
        """ Calculates the state for new comments.
            Newer posts have their comments published automagically.
            Older posts have their default state set to 'DRAFT'
        """
        if (datetime.now() - self.created).days < RECENT_DAYS:
            return PUBLISH_STATES.published
        else:
            return PUBLISH_STATES.draft

    @property 
    def category_descriptions(self):
        """ List of names of each category this page is part of.
        """
        return [c.description for c in self.categories.all()]

    def get_absolute_url(self):
        """ Gets the absolute URL for this entry.
        """
        d = self.created
        return '/blog/%04d-%02d-%02d/%s' % (d.year, d.month, d.day, self.slug)

    @property
    def is_open_for_comment(self):
        """ Is this entry open for new comments?
        """
        return self.allow_comments == ALLOW_COMMENTS.open
            
    @property
    def published_comments(self):
        """ Gets published comments for this entry.
        """
        return self.comment_set.filter(state=PUBLISH_STATES.published) \
                .order_by('created')

    def save(self):
        """ Save this object, setting timestamps as appropriate. """
        if not self.created:
            self.created = datetime.now()
        if not self.id and self.modified:
            # New entry - allow modifed to be set manually
            pass
        else:
            self.modified = datetime.now()
        super(BlogEntry, self).save()

    @property
    def text_as_html(self):
        """ Turn the text into HTML 
        """
        return as_html(self.text, self.transform)

    def __repr__(self):
        """ Make a human readable representation.
        """
        return "BlogEntry(%s): %s" % (self.state, self.title)

    class Admin:
        list_display = ('state', 'title', 'created')
        fields = (
            (None, {
                'fields': ('title', 'state', 'author', 'text',
                    'transform', 'excerpt', 'allow_comments', 'mtId', 
                    'categories', 'slug')
            }),
            ('Times', {
                'classes': 'collapse',
                'fields': ('created', 'modified')
            }),
        )

    class Meta:
        verbose_name_plural= "blog entries"
        ordering = ['-created']

class Comment(models.Model):
    """ A comment on a blog entry.
    """
    created = models.DateTimeField(blank=True, null=True)
    modified = models.DateTimeField(blank=True, null=True)
    state = models.CharField(maxlength=1, blank=True, 
            choices=PUBLISH_STATES.choices())
    text = models.TextField(maxlength=50000, core=True)
    transform = models.CharField(maxlength=2, choices=TRANSFORM_NAMES.choices())
    blog_entry = models.ForeignKey(BlogEntry, edit_inline=models.STACKED)
    author = models.CharField(maxlength=200, core=True)
    email = models.CharField(maxlength=200, blank=True)
    url = models.CharField(maxlength=200, blank=True)

    def get_absolute_url(self):
        """ URL for comment, which always displays on its blog entry's page.
        """
        return self.blog_entry.get_absolute_url() + '#' + self.page_anchor

    @property
    def is_published(self):
        """ Is this comment published?
        """
        return self.state == PUBLISH_STATES.published

    @property
    def page_anchor(self):
        """ HTML Anchor for direct access to comment on this page.
        """
        return self.created.strftime("%Y%m%d%H%M%s")

    def save(self):
        """ Save this object, setting timestamps as appropriate. """
        if not self.created:
            self.created = datetime.now()
        if not self.id and self.modified:
            # New entry - allow modifed to be set manually
            pass
        else:
            self.modified = datetime.now()
        super(Comment, self).save()

    @property
    def text_as_html(self):
        """ Turn the text into HTML 
        """
        return as_html(self.text, self.transform)

    class Admin:
        list_display = ('author', 'state', 'text', 'created')

    class Meta:
        ordering = ['-created']

class Document(models.Model):
    """ A general document to display
    """
    created = models.DateTimeField(blank=True, null=True)
    modified = models.DateTimeField(blank=True, null=True)
    slug = models.SlugField(prepopulate_from=('title',), unique=True)
    state = models.CharField(maxlength=1, blank=True, 
            choices=PUBLISH_STATES.choices())
    title = models.CharField(maxlength=200)
    text = models.TextField(maxlength=50000)
    transform = models.CharField(maxlength=2, 
            choices=TRANSFORM_NAMES.choices(), default=TRANSFORM_NAMES.textile)
    excerpt = models.CharField(maxlength=1000)

    def save(self):
        """ Save this object, setting timestamps as appropriate. """
        if not self.created:
            self.created = datetime.now()
        if not self.id and self.modified:
            # New entry - allow modifed to be set manually
            pass
        else:
            self.modified = datetime.now()
        super(Document, self).save()

    @property
    def text_as_html(self):
        """ Turn the text into HTML 
        """
        return as_html(self.text, self.transform)

    class Admin:
        list_display = ('title', 'modified', 'created')

    class Meta:
        ordering = ['-created']
